package com.isyscore.os.metadata.kettle.vis;

import cn.hutool.core.util.ObjectUtil;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.isyscore.os.core.exception.DataFactoryException;
import com.isyscore.os.metadata.enums.KettleDataFlowNodeType;
import com.isyscore.os.metadata.kettle.base.FlowConfig;
import com.isyscore.os.metadata.kettle.base.FlowConnection;
import com.isyscore.os.metadata.kettle.base.FlowHup;
import com.isyscore.os.metadata.kettle.base.FlowInfo;
import com.isyscore.os.metadata.service.DataSourceService;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

import static com.isyscore.os.core.exception.ErrorCode.*;

public class FlowParser {

    private static final String NODE_TYPE_PROP_NAME = "nodeType";

    @Autowired
    private DataSourceService dataSourceService;

    private Map<KettleDataFlowNodeType, Class<? extends VisNode>> nodeParserMap = Maps.newHashMap();

    private ThreadLocal<List<String>> skipValidateStep = new ThreadLocal();
    public void registerParser(KettleDataFlowNodeType nodeType, Class<? extends VisNode> nodeParser) {
        nodeParserMap.put(nodeType, nodeParser);
    }

    public void setSkipValidateStep(List<String> steps) {
        skipValidateStep.set(steps);
    }

    /**
     * 将前端传入的参数解析为flow配置
     */
    public FlowConfig parseFlow(VisGraph graph,String flowName,boolean checkNodesRule) throws InstantiationException, IllegalAccessException {
        List<VisNode> nodes = Lists.newArrayList();
        List<VisEdge> edges = Lists.newArrayList();
        List<String> distinctName = new ArrayList<>();
        for (Map<String, Object> nodeParam : graph.getNodes()) {
            VisNode visNode = parseNode(nodeParam);
            nodes.add(visNode);
            if(distinctName.contains(visNode.getNodeName())){
                throw new DataFactoryException(NODE_NOT_UNIQUE);
            }
            distinctName.add(visNode.getNodeName());
        }
        for (Map<String, Object> edgeParam : graph.getEdges()) {
            edges.add(parseEdge(edgeParam));
        }
        Map<String, VisNode> nodeMap = nodes.stream().collect(Collectors.toMap(VisNode::getId, node -> node));
        edges.forEach(e -> {
            VisNode sourceNode = nodeMap.get(e.getSourceId());
            VisNode targetNode = nodeMap.get(e.getTargetId());
            if(ObjectUtil.isNull(sourceNode)){
                throw new DataFactoryException(NODE_ID_NOT_FOUND,e.getSourceId());
            }
            if(ObjectUtil.isNull(targetNode)){
                throw new DataFactoryException(NODE_ID_NOT_FOUND,e.getTargetId());
            }
            sourceNode.addOutgoingNode(targetNode);
            targetNode.addIncomingNode(sourceNode);
        });

        if(checkNodesRule){
            checkNodesRule(nodes);
        }

        FlowInfo flowInfo = new FlowInfo();

        /**
         *构建流程配置
         */

        FlowConfig flowConfig = new FlowConfig();

        flowInfo.setName(flowName);
        //base
        flowConfig.setTransInfo(flowInfo);
        flowConfig.setSteps(nodes);
        //hup
        List<FlowHup> flowHups = edges.stream().map(e -> new FlowHup(nodeMap.get(e.getSourceId()).getNodeName(), nodeMap.get(e.getTargetId()).getNodeName())).collect(Collectors.toList());
        flowConfig.setTransHups(flowHups);
        //connection
        Set activeNode = new HashSet();
//        for (FlowHup flowHup : flowHups) {
//            activeNode.add(flowHup.getFrom());
//            activeNode.add(flowHup.getTo());
//        }
//        Set<String> dbConnections = nodes.stream().filter(n->activeNode.contains(n.getNodeName()) && n.getDbSourceId()!=null && n.getDbSourceId()!=0).map(n -> (n.getDbSourceId()+":"+n.getDatabaseName())).collect(Collectors.toSet());

        Set<String> dbConnections = nodes.stream().filter(n-> n.getDbSourceId()!=null && n.getDbSourceId()!=0).map(n -> (n.getDbSourceId()+":"+n.getDatabaseName())).collect(Collectors.toSet());

        List<FlowConnection> flowConnections = dbConnections.stream().map(id -> new FlowConnection().fill(dataSourceService.getDataSource(Long.parseLong(id.split(":")[0])),id.split(":")[1])).collect(Collectors.toList());
        flowConfig.setTransConnections(flowConnections);


        flowConfig.setOtherInfo(graph.getRenderConfig());
        flowConfig.setOtherParam(graph.getOtherParam());
        return flowConfig;
    }

    private VisNode parseNode(Map<String, Object> nodeParam) throws InstantiationException, IllegalAccessException {
        String nodeType = (String) nodeParam.get(NODE_TYPE_PROP_NAME);
        if (nodeType == null) {
            throw new DataFactoryException(VIS_CONFIG_ERROR_NODE_TYPE_NOT_FOUND);
        }
        KettleDataFlowNodeType nodeTypeEnum = KettleDataFlowNodeType.valueOf(nodeType);
        Class<? extends VisNode> nodeParserClass = nodeParserMap.get(nodeTypeEnum);
        if (nodeParserClass == null) {
            throw new DataFactoryException(VIS_CONFIG_ERROR_NODE_TYPE_NOT_REGISTER, nodeType);
        }
        VisNode node = nodeParserClass.newInstance();
        node.valueOf(nodeParam);
        return node;
    }

    private VisEdge parseEdge(Map<String, Object> edgeParam) {
        VisEdge edge = new VisEdge();
        edge.valueOf(edgeParam);
        return edge;
    }

    private VisEdge parseInfo(Map<String, Object> edgeParam) {
        VisEdge edge = new VisEdge();
        edge.valueOf(edgeParam);
        return edge;
    }

    private void checkNodesRule(List<VisNode> nodes) {
        nodes.stream().filter(n->(skipValidateStep.get()==null||!skipValidateStep.get().contains(n.getNodeName()))).forEach(n -> {
            String err = n.validate();
            if (!Strings.isNullOrEmpty(err)) {
                throw new DataFactoryException(VIS_CONFIG_PARSE_ERROR, n.getNodeName(), err);
            }
        });

        skipValidateStep.remove();
    }

    /**
     * 根据参数校验单个节点
     * @param nodeParam 节点参数
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public String validateNode(Map<String, Object> nodeParam) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        String nodeType = (String) nodeParam.get(NODE_TYPE_PROP_NAME);
        if (nodeType == null) {
            throw new DataFactoryException(VIS_CONFIG_ERROR_NODE_TYPE_NOT_FOUND);
        }
        KettleDataFlowNodeType nodeTypeEnum = KettleDataFlowNodeType.valueOf(nodeType);
        Class<? extends VisNode> nodeParserClass = nodeParserMap.get(nodeTypeEnum);
        Constructor<? extends VisNode> c=nodeParserClass.getDeclaredConstructor();
        if (nodeParserClass == null) {
            throw new DataFactoryException(VIS_CONFIG_ERROR_NODE_TYPE_NOT_REGISTER, nodeType);
        }
        c.setAccessible(true);
        VisNode node = c.newInstance();
        node.valueOf(nodeParam);
        return node.validate();
    }
}
